// standard libraries
#include <vector>

// wii libraries
#include <ogc/lwp_watchdog.h>

// custom libraries
#include "..\..\include\graphics\graphics_renderer.h"
#include "..\..\include\graphics\image.h"

#include "..\..\include\game_manager.h"
#include "..\..\include\resource_manager.h"
#include "..\..\include\user_controller.h"

#include "..\..\include\game_state\exit_state.h"
#include "..\..\include\game_state\menu_state.h"
#include "..\..\include\game_state\intro_state.h" // matchng header


// ==================================
//     SINGLETON IMPLEMENTATION
// ==================================

IntroState* IntroState::Instance()
{
	// Meyers singleton
	static IntroState obj;
	return &obj;
}

IntroState::IntroState() 
{
	timesOpened_ = 0;

	durationScreen_  = 5000;  // <-- 
	durationFadeIn_  = 700;   // <-- Milliseconds
	durationFadeOut_ = 700;   // <--

	// Note: Be careful if setting durationFadeIn_ or durationFadeOut_ to 0.
	// They are used as denominators in IntroState::render().
}

IntroState::IntroState(const IntroState& other) {}
IntroState::~IntroState() {}
IntroState& IntroState::operator=(const IntroState& other) { return *this; }


// ==================================
//          OPEN and CLOSE
// ==================================

void IntroState::onOpen()
{
	if (timesOpened_++ > 0)
		return;

	ResourceManager* rm = ResourceManager::Instance();

	// Hold Wii Remote sideways screen (screen 0)
	extern const char wii_strap_png[];
	extern const unsigned int wii_strap_png_size;
	Image* wii_strap = rm->getImage(wii_strap_png, wii_strap_png_size);
	screens_.push_back(wii_strap);

	// GRRLIB logo (screen 1)
	extern const char grrlib_logo_png[];
	extern const unsigned int grrlib_logo_png_size;
	Image* grrlib_logo = rm->getImage(grrlib_logo_png, grrlib_logo_png_size);
	screens_.push_back(grrlib_logo);

	currentScreen_ = 0;
	currentScreenBeginTime_ = 0;
}

void IntroState::onClose()
{
	if (--timesOpened_ > 0)
		return;

	for (int i = 0, j = screens_.size(); i < j; i++)
		ResourceManager::Instance()->release(screens_[i]);
	screens_.clear();
}


// ==================================
//          ENTER and EXIT
// ==================================

void IntroState::onEnter() {}
void IntroState::onExit() {}


// ==================================
//         UPDATE and RENDER
// ==================================

void IntroState::update(GameManager* game, UserController* userController)
{
	// Function updates the current screen that has to be diaplyed by either
	// keeping track of time and changing the screen when enough time has 
	// passed, or by listening to user input.

	long currentTime = (long)(gettime() / TB_TIMER_CLOCK); 

	// Variable currentScreenBeginTime_ is 0 only if this is the first time 
	// IntroState singleton class has been updated after calling onOpen().
	if (currentScreenBeginTime_ == 0)
		currentScreenBeginTime_ = currentTime;

	userController->scanButtons();

	bool shouldChangeScreen = 
		userController->isButtonPressed(UserController::SELECT) ||
		(currentTime >= currentScreenBeginTime_ + durationScreen_);

	if (shouldChangeScreen) {
		currentScreenBeginTime_ = currentTime;
		if (++currentScreen_ == screens_.size()) {
			game->changeState(ExitState::Instance());
			game->pushState(MenuState::Instance());
		}
	}
}

void IntroState::render(GraphicsRenderer* graphicsRenderer)
{
	long currentTime = (long)(gettime() / TB_TIMER_CLOCK);
	long currInterval = currentTime - currentScreenBeginTime_;

	bool shouldFadeIn  = currInterval <= durationFadeIn_;
	bool shouldFadeOut = currInterval >= durationScreen_ - durationFadeOut_;

	// Amount ranges from 0.0 to 1.0, the latter being 100 percent. If it goes
	// over 1.0 or under 0.0 the bool flags above would be false, in which case
	// these amount values wouldn't be used.
	double amountFadeIn  = 1.0 - (double)currInterval / (double)durationFadeIn_;
	double amountFadeOut = 1.0 - (double)(durationScreen_ - currInterval) / 
		                         (double)durationFadeOut_;

	graphicsRenderer->drawImage(*screens_[currentScreen_], 0, 0);

	if (shouldFadeIn) {
		unsigned int alpha = (unsigned int)(255.0 * amountFadeIn);
		graphicsRenderer->fillScreen(alpha);
	} else if (shouldFadeOut) {
		unsigned int alpha = (unsigned int)(255.0 * amountFadeOut);
		graphicsRenderer->fillScreen(alpha);
	}

	graphicsRenderer->render();
}
